package com.bbcare.followup.plat.service.impl;

import java.io.*;
import java.util.*;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;

import com.bbcare.comm.PropertiesUtils;
import com.bbcare.comm.SeqUtil;
import com.bbcare.followup.plat.service.IHisService;


@Service
public class HisServiceImpl implements IHisService {
    private final static Logger logger = Logger.getLogger(HisServiceImpl.class);
    private final static String SQL_QUERY_INPATIENT_LIST    = "SELECT * FROM his_patient WHERE patient_inout='I' AND tenant_id=?";
    private final static String SQL_QUERY_INPATIENT_INFO    = "SELECT * FROM his_patient WHERE patient_inout='I' AND tenant_id=? AND patient_id=?";
    private final static String SQL_QUERY_INPATIENT_HIS_LIST= "SELECT * FROM his_inpatient_his WHERE tenant_id=? AND patient_id=?";
    private final static String SQL_QUERY_INPATIENT_PHONE   = "SELECT phone FROM his_patient WHERE patient_inout='I' AND tenant_id=? AND patient_id=?";
    private final static String SQL_UPDATE_INPATIENT_PHONE  = "UPDATE his_patient SET phone=? WHERE patient_inout='I' AND tenant_id=? AND patient_id=?";
    private final static String SQL_QUERY_PATIENT_HISID     = "SELECT his_id FROM his_id_mapper WHERE tenant_id=? AND followup_id=?";

    @Autowired
    private JdbcTemplate jdbc;
    @Autowired
    private NamedParameterJdbcTemplate namedJdbc;


    /**
     * 解析患者详情。
     * @param soapResponse
     * @return
     * @throws Exception
     */
    @Override
    public Map getPatientInfoFromSoap(final String soapResponse) throws Exception {
        Document docu = DocumentHelper.parseText(soapResponse);
        Element root = docu.getRootElement();
        String cdata;
        Element patEle;
        Attribute useAttr,valueAttr;
        Map retMap  = new HashMap();
        if (null != root.element("Body").element("QueryDetailedResponse").element("QueryDetailedResult")) {
            cdata   = root.element("Body").element("QueryDetailedResponse").element("QueryDetailedResult").getTextTrim();
            docu    = DocumentHelper.parseText(cdata);
            if (docu.getRootElement().element("StringValue") != null) {
                cdata   = docu.getRootElement().element("StringValue").getTextTrim();
                docu = DocumentHelper.parseText(cdata);
                if (docu.getRootElement().element("controlActProcess").element("subject").element("registrationEvent").element("subject1").element("patient")!=null) {
                    patEle  = docu.getRootElement().element("controlActProcess").element("subject").element("registrationEvent").element("subject1").element("patient");
                    List<Element> itemList  = patEle.element("telecom").elements("item");
                    for(Element ele : itemList)   {
                        useAttr = ele.attribute("use");
                        valueAttr   = ele.attribute("value");
                        if (useAttr.getStringValue().equalsIgnoreCase("PUB") && !StringUtils.isBlank(valueAttr.getStringValue()))   {
                            retMap.put(MAPPER_PHONE, valueAttr.getStringValue());
                        }
                    }
                }
            }
        }

        return retMap;
    }

    /**
     * 解析患者列表。
     *
     * @param soapResponse 从广二HIS返回的XML数据字符串。
     * @return
     * @throws Exception
     */
    @Override
    public List<Map> getPatientListFromSoap(final String soapResponse) throws Exception {
        List<Map> retList   = new ArrayList<>();
        Document docu = DocumentHelper.parseText(soapResponse);
        Element root = docu.getRootElement();
        List<Element> elements;
        String cdata,patId,inpNo,patName,sex,birthday,addr,inDate,outDate,deptCode,deptName,bedNo,areaCode,areaName;
        Map<String,String> patMap;
        if (null != root.element("Body").element("QueryListResponse").element("QueryListResult")) {
            cdata   = root.element("Body").element("QueryListResponse").element("QueryListResult").getTextTrim();
            docu = DocumentHelper.parseText(cdata);
            if (docu.getRootElement().element("StringValue") != null) {
                cdata   = docu.getRootElement().element("StringValue").getTextTrim();
                docu = DocumentHelper.parseText(cdata);
                if (docu.getRootElement().element("VALUES")!=null) {
                    elements    = docu.getRootElement().element("VALUES").elements("PATIENT");
                    for (Element patEle : elements) {
                        patMap  = new HashMap<>();
                        patId   = patEle.element("PATIENT_ID").getTextTrim();   //患者ID（住院号），多次住院相同。
                        patMap.put(MAPPER_PATIENT_ID, patId);

                        inpNo   = patEle.element("INP_NO").getTextTrim();   //流水号，多次住院是不同的。
                        patMap.put(MAPPER_IN_SEQNO, inpNo);

                        patName = patEle.element("NAME").getTextTrim();
                        patMap.put(MAPPER_PATIENT_NAME, patName);

                        sex     = patEle.element("SEX").getTextTrim();
                        if (sex.equals("男")) {
                            patMap.put(MAPPER_GENDER, MAPPER_GENDER_MALE);
                        } else {
                            patMap.put(MAPPER_GENDER, MAPPER_GENDER_FEMALE);
                        }

                        birthday= patEle.element("BIRTHDAY").getTextTrim();
                        patMap.put(MAPPER_BIRTHDAY, birthday);

                        addr    = patEle.element("ADDRESS").getTextTrim();
                        patMap.put(MAPPER_ADDRESS, addr);

                        inDate  = patEle.element("IN_TIME").getTextTrim();  //入院日期。
                        patMap.put(MAPPER_INDATE, inDate);

                        outDate = patEle.element("OUT_TIME").getTextTrim(); //出院日期。
                        patMap.put(MAPPER_OUTDATE, outDate);

                        deptCode= patEle.element("DEPT_CODE").getTextTrim();//入院科室。
                        patMap.put(MAPPER_DEPTCODE, deptCode);

                        deptName= patEle.element("DEPT_NAME").getTextTrim();//科室名称。
                        patMap.put(MAPPER_DEPTNAME, deptName);

                        bedNo   = patEle.element("BED_NO").getTextTrim();
                        patMap.put(MAPPER_BEDNO, bedNo);

                        areaCode= patEle.element("AREA_CODE").getTextTrim();//病区编码。
                        patMap.put(MAPPER_AREA_CODE, areaCode);
                        areaName= patEle.element("AREA_NAME").getTextTrim();//病区名称。
                        patMap.put(MAPPER_AREA_NAME, areaName);

                        patMap.put(MAPPER_PHONE, "");

                        retList.add(patMap);
                    }
                }
            }
        }

        return retList;
    }

    /**
     * 从HIS查询模板文件中读取并生成查询SOAP参数。
     * @param templateFilename
     * @return
     * @throws Exception
     */
    @Override
    public String getResourceFile(final String templateFilename) throws Exception {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources= resolver.getResources("his/" + templateFilename);
        Resource resource   = resources[0];
        StringBuilder sb = new StringBuilder();
        BufferedReader bufReader    = null;
        InputStreamReader inReader  = null;
        FileInputStream in  = null;
        String line = null;
        try {
            File file = resource.getFile();
            in  = new FileInputStream(file);
            inReader    = new InputStreamReader(in, "UTF-8");
            bufReader   = new BufferedReader(inReader);
            while ((line = bufReader.readLine()) != null) {
                sb.append(line);
            }
        } catch (Exception ex)  {
            logger.error(ex.getMessage());
        } finally {
            if (null != bufReader)   {
                bufReader.close();
            }
            if (null != inReader) {
                inReader.close();
            }
            if (null != in) {
                in.close();
            }
        }
        return sb.toString();
    }

    /**
     * 保存住院患者列表。
     * @param tenantId
     * @param patientList
     */
    @Override
    public void saveInPatientList(final String tenantId, final List<Map> patientList)   {
        String patId,seqno,patName,gender,birthday,phone,querySql,insertSql,updateSql,id,deptCode,deptName,bedNo,areaCode,areaName,inDate,outDate;
        final int expect    = patientList.size();
        int success=0,fail=0,inserted=0,updated=0;
        for(Map apatient : patientList)   {
            patId   = apatient.get(MAPPER_PATIENT_ID).toString();
            patName = apatient.get(MAPPER_PATIENT_NAME).toString();
            gender  = apatient.get(MAPPER_GENDER).toString();
            birthday= apatient.get(MAPPER_BIRTHDAY).toString();
            phone   = apatient.get(MAPPER_PHONE).toString();
            seqno   = apatient.get(MAPPER_IN_SEQNO).toString();
            inDate  = apatient.get(MAPPER_INDATE).toString();
            outDate = apatient.get(MAPPER_OUTDATE).toString();
            deptCode= apatient.get(MAPPER_DEPTCODE).toString();
            deptName= apatient.get(MAPPER_DEPTNAME).toString();
            areaCode= apatient.get(MAPPER_AREA_CODE).toString();
            areaName= apatient.get(MAPPER_AREA_NAME).toString();
            bedNo   = apatient.get(MAPPER_BEDNO).toString();
            querySql= "SELECT COUNT(*) FROM his_patient WHERE patient_inout='I' AND tenant_id='" + tenantId + "' AND patient_id='" + patId + "'";
            int total   = jdbc.queryForObject(querySql, Integer.class);
            if (0 == total) {
                //新增的患者。
                id  = SeqUtil.getSeqNo("guanger_");
                insertSql   = "INSERT INTO his_patient (id, tenant_id, patient_id, patient_inout, name, gender, birthday, phone) VALUES (?,?,?,?,?,?,?,?)";
                try {
                    inserted = jdbc.update(insertSql, id, tenantId, patId, "I", patName, gender, birthday, phone);
                } catch (DataAccessException dae)   {
                    logger.error(dae.getMessage());
                    continue;
                }
                if (1 == inserted)  {
                    logger.info("++++saveInPatientList() succeed to insert patient id:"+patId+", name:"+patName);
                    success++;
                } else {
                    fail++;
                    logger.error("----saveInPatientList() fail to insert patient id:"+patId+", name:"+patName);
                }
                //保存到住院历史表。
                inserted    = 0;
                id  = SeqUtil.getSeqNo("guanger_");
                insertSql   = "INSERT INTO his_inpatient_his (id, tenant_id, patient_id, in_seqno, dept_code, dept_name, indate, outdate, area_code, area_name, bedno) VALUES (?,?,?,?,?,?,?,?,?,?,?)";
                try {
                    inserted = jdbc.update(insertSql, id, tenantId, patId, seqno, deptCode, deptName, inDate, StringUtils.isBlank(outDate) ? "9999-12-31" : outDate, areaCode, areaName, bedNo);
                } catch (DataAccessException dae)   {
                    logger.error(dae.getMessage());
                }
                if (1 == inserted)  {
                    logger.info("++++saveInPatientList() succeed to insert patient id:"+patId+", seqno:"+seqno+" to his_inpatient_his.");
                    success++;
                } else {
                    fail++;
                    logger.error("----saveInPatientList() succeed to insert patient id:"+patId+", seqno:"+seqno+" to his_inpatient_his.");
                }
            } else {
                //老患者更新数据。
                updated = 0;
                updateSql   = "UPDATE his_patient SET name=?, gender=?, birthday=?, phone=? WHERE patient_inout='I' AND tenant_id=? AND patient_id=?";
                try {
                    updated = jdbc.update(updateSql, patName, gender, birthday, phone, tenantId, patId);
                } catch (DataAccessException dae)   {
                    logger.error(dae.getMessage());
                    continue;
                }
                if (1 == updated)  {
                    logger.info("++++saveInPatientList() succeed to update patient id:"+patId+", name:"+patName);
                    success++;
                } else {
                    logger.error("----saveInPatientList() fail to update patient id:"+patId+", name:"+patName);
                    fail++;
                }

                updated = 0;
                updateSql   = "UPDATE his_inpatient_his SET dept_code=?, dept_name=?, indate=?, outdate=?, area_code=?, area_name=?, bedno=? WHERE tenant_id=? AND patient_id=?";
                try {
                    updated = jdbc.update(updateSql, deptCode, deptName, inDate, StringUtils.isBlank(outDate) ? "9999-12-31" : outDate, areaCode, areaName, bedNo, tenantId, patId);
                } catch (DataAccessException dae)   {
                    logger.error(dae.getMessage());
                }
                if (1 == updated)  {
                    logger.info("++++saveInPatientList() succeed to update patient id:"+patId+", seqno:"+seqno+" to his_inpatient_his");
                    success++;
                } else {
                    logger.error("----saveInPatientList() fail to update patient id:"+patId+", seqno:"+seqno+" to his_inpatient_his");
                    fail++;
                }
            }
        }
        logger.info("++++saveInPatientList() patients:"+expect*2+", success:"+success+", fail:"+fail);
    }

    /**
     * 增加超时时间限制
     *
     * @param httpClient
     */
    private static void initTimeout(HttpClient httpClient) {
        int myTimeout   = 30000;    // default 60 seconds.
        final String timeoutConf    = PropertiesUtils.getProperty("his_connection_timeout");
        if (StringUtils.isNotEmpty(timeoutConf) && StringUtils.isNotBlank(timeoutConf)) {
            try {
                myTimeout   = Integer.parseInt(timeoutConf);
            } catch (Exception ex)  {
                logger.error(ex.getMessage());
            }
        }
        httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(myTimeout);
        httpClient.getHttpConnectionManager().getParams().setSoTimeout(myTimeout);
        httpClient.getParams().setSoTimeout(myTimeout);
    }

    @Override
    public String sendMessageToHis(final String hisUrl, final String xmlMsg, final String soapAction) throws Exception {
        logger.info(xmlMsg);
        String resp = null;
        PostMethod method = null;
        try {
            long begin = System.currentTimeMillis();
            HttpClient httpClient = new HttpClient();
            initTimeout(httpClient);
            method = new PostMethod(hisUrl);
            if (StringUtils.isNotEmpty(soapAction) && StringUtils.isNotBlank(soapAction)) {
                method.setRequestHeader("SOAPAction", soapAction);
            }
            method.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
            RequestEntity re = new StringRequestEntity(xmlMsg, "text/xml", "utf-8");
            method.setRequestEntity(re);
            httpClient.executeMethod(method);
            InputStream in = method.getResponseBodyAsStream();
            resp    = IOUtils.toString(in, "utf-8");
            IOUtils.closeQuietly(in);
            int statusCode = method.getStatusLine().getStatusCode();
            //assertResultSuccess(statu_code);
            //logger.warn("响应报文为：" + result);
            logger.warn("发送请求完成，耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
        } catch (Exception ex) {
            logger.error("对端接口发送失败！", ex);
            throw ex;
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }

        return resp.replaceAll("\r|\n", "");
    }

    /**
     * 查询住院患者PHONE。
     * @param tenantId
     * @param patientId
     * @return null患者不存在。
     */
    @Override
    public String queryInPatientPhone(final String tenantId, final String patientId)   {
        List<Map<String,Object>> resultList = jdbc.queryForList(SQL_QUERY_INPATIENT_PHONE, tenantId, patientId);
        if (null != resultList && 0 != resultList.size()) {
            return resultList.get(0).get("phone").toString();
        } else {
            return null;
        }
    }

    @Override
    public int updateInPatientPhone(final String phone, final String tenantId, final String patientId)   {
        int updated = jdbc.update(SQL_UPDATE_INPATIENT_PHONE, phone, tenantId, patientId);
        return updated;
    }

    /**
     * 查询住院患者列表。
     * @param tenantId
     */
    @Override
    public List<Map<String,Object>> queryInPatientList(final String tenantId) {
        final String docuLinkConf   = PropertiesUtils.getProperty("his_guanger_docu_link");
        List<Map<String,Object>> retList    = jdbc.queryForList(SQL_QUERY_INPATIENT_LIST, tenantId);
        String docuLink, patientId;
        for(Map<String,Object> patientMap : retList)   {
            if (!patientMap.containsKey(MAPPER_DOCU_LINK) || StringUtils.isBlank(patientMap.get(MAPPER_DOCU_LINK).toString()))  {
                patientId   = patientMap.get("patient_id").toString();
                docuLink    = docuLinkConf.replaceAll("PATIENTID", patientId);
                patientMap.put(MAPPER_DOCU_LINK, docuLink);
            }
        }

        return retList;
    }

    /**
     * 查询住院患者详情。
     * @param tenantId
     * @param patientId
     * @return
     */
    @Override
    public Map<String,Object> queryInPatientInfo(final String tenantId, final String patientId) {
        final String docuLinkConf   = PropertiesUtils.getProperty("his_guanger_docu_link");
        Map<String,Object> patientMap   = jdbc.queryForMap(SQL_QUERY_INPATIENT_INFO, tenantId, patientId);
        final String docuLink = docuLinkConf.replaceAll("PATIENTID", patientId);
        patientMap.put(MAPPER_DOCU_LINK, docuLink);

        return patientMap;
    }

    /**
     * 查询患者住院历史。
     * @param tenantId
     * @return
     */
    @Override
    public List<Map<String,Object>> queryInPatientHisList(final String tenantId, final String patientId) {
        List<Map<String,Object>> inpatList  = jdbc.queryForList(SQL_QUERY_INPATIENT_HIS_LIST, tenantId, patientId);
        String outdateStr;
        for(Map inpatMap : inpatList)   {
            //处理出院日期。
            outdateStr  = inpatMap.get("outdate").toString();   //出院日期。
            if (outdateStr.equals("9999-12-31"))   {
                inpatMap.put("outdate","");
            }
        }

        return inpatList;
    }

    /**
     * 保存随访系统ID和医院HIS系统ID的映射。
     * @param tenantId
     * @param followupId
     * @param hisId
     * @return
     */
    @Override
    public int saveIdMapper(final String tenantId, final String followupId, final String hisId)   {
        final String insertSql   = "INSERT INTO his_id_mapper (followup_id, tenant_id, his_id) VALUES (?,?,?)";
        int inserted = 0;
        try {
            inserted = jdbc.update(insertSql, followupId, tenantId, hisId);
        } catch (DataAccessException dae)   {
            logger.error(dae.getMessage());
        }
        return inserted;
    }

    /**
     * 查询随访系统患者的医疗文书链接。
     * @param tenantId
     * @param followupId
     * @return
     */
    @Override
    public String queryDocuLink(final String tenantId, final String followupId)   {
        List<Map<String,Object>> resultList = jdbc.queryForList(SQL_QUERY_PATIENT_HISID, tenantId, followupId);
        String hisId    = "";
        if (null != resultList && 0 != resultList.size()) {
            hisId   = resultList.get(0).get("his_id").toString();
        }
        final String docuLinkConf   = PropertiesUtils.getProperty("his_guanger_docu_link");
        String actualLink   = "";
        if (StringUtils.isNotEmpty(docuLinkConf) && StringUtils.isNotBlank(docuLinkConf))   {
            actualLink  = docuLinkConf.replaceAll("PATIENTID", hisId);
        }

        return actualLink;
    }

    /**
     * 根据条件模糊搜索患者。
     * @param paraMap
     *  "tenantId"
     *  "username"  患者姓名
     *  "phone"     联系电话
     * @return
     */
    @Override
//    public List<Map<String,Object>> queryByCondition(final Map<String,Object> paraMap)   {
    public Map<String,Object> queryByCondition(final Map<String,Object> paraMap)   {
        final String tenantId   = paraMap.get("tenantId").toString();
        final String username   = paraMap.get("username").toString();
        StringBuffer whereSb    = new StringBuffer();
        whereSb.append(" WHERE tenant_id='").append(tenantId).append("'");
        if (StringUtils.isNotBlank(username))   {
            //根据患者姓名模糊查询。
            whereSb.append(" AND name LIKE '%").append(username).append("%'");
        }

        final String phone  = paraMap.get("phone").toString();
        if (StringUtils.isNotBlank(phone))   {
            //根据患者姓名模糊查询。
            whereSb.append(" AND phone LIKE '%").append(phone).append("%'");
        }

        Integer start   = (Integer)paraMap.get("start");
        Integer end     = (Integer)paraMap.get("end");
        final String limit  = " LIMIT " + start.toString() + "," + end.toString();
        final String querySql   = "SELECT * FROM his_patient " + whereSb.toString() + limit;
        final String countSql   = "SELECT COUNT(*) FROM his_patient " + whereSb.toString();
        logger.info("++++queryByCondition() querySql:"+querySql);
        logger.info("++++queryByCondition() countSql:"+countSql);
        final Integer total   = jdbc.queryForObject(countSql, Integer.class);
        List<Map<String,Object>> retList    = jdbc.queryForList(querySql);
        final String docuLinkConf   = PropertiesUtils.getProperty("his_guanger_docu_link");
        String patientId,docuLink;
        for(Map<String,Object> patientMap : retList)   {
            if (!patientMap.containsKey(MAPPER_DOCU_LINK) || StringUtils.isBlank(patientMap.get(MAPPER_DOCU_LINK).toString()))  {
                patientId   = patientMap.get("patient_id").toString();
                docuLink    = docuLinkConf.replaceAll("PATIENTID", patientId);
                patientMap.put(MAPPER_DOCU_LINK, docuLink);
            }
        }

        Map<String,Object> retMap   = new HashMap<>();
        retMap.put("total",     total);
        retMap.put("rowlist",   retList);
        return retMap;
    }
}
